#include "stdafx.h"
#include <windows.h>
#include "debugMonitor.h"
#include <wchar.h>
#include "debug.h"
#include "Marco.h"

using namespace std;

CWinDebugMonitor::~CWinDebugMonitor()
{
	Unintialize();
}

CWinDebugMonitor::CWinDebugMonitor()
: m_lastID(0)
, m_newLine(true)
, m_hEventClose(NULL)
, m_Callback(NULL)
{
}


DWORD CWinDebugMonitor::Initialize(CComBSTR prefix)
{
    DWORD errorCode = 0;
    BOOL bSuccessful = FALSE;

    SetLastError(0);

	SECURITY_ATTRIBUTES sa;
	SECURITY_DESCRIPTOR sd;
	sa.nLength = sizeof(SECURITY_ATTRIBUTES); 
	sa.bInheritHandle = FALSE; 
	sa.lpSecurityDescriptor = &sd;


	if(::InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) == FALSE)
	{ 
		errorCode = GetLastError();
        return errorCode;
	} 

	if(::SetSecurityDescriptorDacl(&sd, TRUE, (PACL)NULL, FALSE) == FALSE) 
	{ 
		errorCode = GetLastError();
        return errorCode;
	} 

	// Event: close
	m_hEventClose = ::CreateEvent(NULL, TRUE, FALSE, NULL);

    // Event: buffer ready
    // ---------------------------------------------------------
    CComBSTR DBWIN_BUFFER_READY = prefix;
	DBWIN_BUFFER_READY += L"DBWIN_BUFFER_READY";
    m_hEventBufferReady = ::OpenEvent(
        EVENT_ALL_ACCESS,
        FALSE,
        DBWIN_BUFFER_READY
        );

    if (m_hEventBufferReady == NULL) {
        m_hEventBufferReady = ::CreateEvent(
            &sa,
            FALSE,    // auto-reset
            TRUE,    // initial state: signaled
            DBWIN_BUFFER_READY
            );

        if (m_hEventBufferReady == NULL) {
            errorCode = GetLastError();
            return errorCode;
        }
    }

    // Event: data ready
    // ---------------------------------------------------------
    CComBSTR DBWIN_DATA_READY = prefix;
	DBWIN_DATA_READY += L"DBWIN_DATA_READY";
    m_hEventDataReady = ::OpenEvent(
        SYNCHRONIZE,
        FALSE,
        DBWIN_DATA_READY
        );

    if (m_hEventDataReady == NULL) {
        m_hEventDataReady = ::CreateEvent(
            &sa,
            FALSE,    // auto-reset
            FALSE,    // initial state: nonsignaled
            DBWIN_DATA_READY
            );

        if (m_hEventDataReady == NULL) {
            errorCode = GetLastError();
            return errorCode;
        }
    }

    // Shared memory
    // ---------------------------------------------------------
    CComBSTR DBWIN_BUFFER = prefix;
	DBWIN_BUFFER += L"DBWIN_BUFFER";
    m_hDBMonBuffer = ::OpenFileMapping(
        FILE_MAP_READ,
        FALSE,
        DBWIN_BUFFER
        );

    if (m_hDBMonBuffer == NULL) {
        m_hDBMonBuffer = ::CreateFileMapping(
            INVALID_HANDLE_VALUE,
            &sa,
            PAGE_READWRITE,
            0,
            sizeof(SDBWinBuffer),
            DBWIN_BUFFER
            );

        if (m_hDBMonBuffer == NULL) {
            errorCode = GetLastError();
            return errorCode;
        }
    }

    m_pDBBuffer = (SDBWinBuffer *)::MapViewOfFile(
        m_hDBMonBuffer,
        SECTION_MAP_READ,
        0,
        0,
        0
        );

    if (m_pDBBuffer == NULL) {
        errorCode = GetLastError();
        return errorCode;
    }

    // Monitoring thread
    // ---------------------------------------------------------
    m_hWinDebugMonitorThread = ::CreateThread(
        NULL,
        0,
		&CWinDebugMonitor::WinDebugMonitorThread,
        this,
        0,
        NULL
        );

    if (m_hWinDebugMonitorThread == NULL) {
        errorCode = GetLastError();
        return errorCode;
    }

    // set monitor thread's priority to highest
    // ---------------------------------------------------------
    bSuccessful = ::SetPriorityClass(
        ::GetCurrentProcess(),
        REALTIME_PRIORITY_CLASS
        );

    bSuccessful = ::SetThreadPriority(
        m_hWinDebugMonitorThread,
        THREAD_PRIORITY_TIME_CRITICAL
        );

	return 0;
}

void CWinDebugMonitor::Unintialize()
{
    if (m_hWinDebugMonitorThread != NULL)
	{
		if( m_hEventClose != NULL )
		{
			::SetEvent(m_hEventClose);
			::WaitForSingleObject(m_hWinDebugMonitorThread, INFINITE);

			SAFE_CLOSE_HANDLE(m_hEventClose);
		}
    }

    if (m_hDBMonBuffer != NULL)
	{
        ::UnmapViewOfFile(m_pDBBuffer);
        CloseHandle(m_hDBMonBuffer);
        m_hDBMonBuffer = NULL;
    }
	SAFE_CLOSE_HANDLE(m_hEventBufferReady);
	SAFE_CLOSE_HANDLE(m_hEventDataReady);

    m_pDBBuffer = NULL;
}


DWORD CWinDebugMonitor::WinDebugMonitorProcess()
{
    DWORD ret = 0;

    // wait for data ready
    ret = ::WaitForSingleObject(m_hEventDataReady, TIMEOUT_WIN_DEBUG);

    if (ret == WAIT_OBJECT_0) {

		DWORD pid = *(DWORD *)m_pDBBuffer; 
		if (m_lastID != pid)
		{
			m_lastID = pid;
		}

		if (!m_newLine)
		{
			//dprintf(m_file, "\n");
		}

		char *head = m_pDBBuffer->data;
		char *tail = strlen(m_pDBBuffer->data) + m_pDBBuffer->data - 1;
		char *newline = NULL;

		m_newLine = false;
		while(1)
		{
			//fprintf(m_file, "%.8f [%d]", (curClock.QuadPart - m_startClock.QuadPart)/(float)m_cpuFreq.QuadPart, pid);
			newline = strstr(head, "\n");

			if(newline)
			{
				//fprintf(m_file, "%.*s", newline-head+1, head);
				string s = "";
				s.assign(head, newline-head+1);
				//dprintf("[XXXX] new string:'%s', original:'%s'", s.c_str(), head);
				if(m_Callback)
				{
					m_Callback( pid, s.c_str() );
				}

				dprintf("%.*s", newline-head+1, head);
				head = newline + 1;
			}
			else
			{
				dprintf("%s", head);
				break;
			}

			if (newline == tail)
			{
				m_newLine = true;
				break;
			}
			if (head > tail)
				break;
		}
        //OutputWinDebugString(m_pDBBuffer->data);

        // signal buffer ready
        SetEvent(m_hEventBufferReady);
    }

    return ret;
} 

DWORD WINAPI CWinDebugMonitor::WinDebugMonitorThread(void *pData)
{
    CWinDebugMonitor *_this = (CWinDebugMonitor *)pData;

    if (_this != NULL) {
		// if m_hEventClose is signal, exit the thread
		while( WAIT_OBJECT_0 != WaitForSingleObject( _this->m_hEventClose, 0 ) )
		{
            _this->WinDebugMonitorProcess();
        }
    }

    return 0;
}

